package com.livepan.server.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.livepan.server.dao.SubmissionDOMapper;
import com.livepan.server.dataobject.SubmissionDO;
import com.livepan.server.dataobject.SubmissionDOExample;
import com.livepan.server.dataobject.SubmissionDOWithBLOBs;
import com.livepan.server.error.BusinessException;
import com.livepan.server.error.EmBusinessError;
import com.livepan.server.service.ContestService;
import com.livepan.server.service.ProblemService;
import com.livepan.server.service.SubmissionService;
import com.livepan.server.service.model.ProblemModel;
import com.livepan.server.service.model.SubmissionModel;
import com.livepan.server.utils.AsyncUtils;
import com.livepan.server.utils.ConstUtil;
import com.livepan.server.utils.PushJudgerUtil;
import com.livepan.server.validator.ValidationResult;
import com.livepan.server.validator.ValidatorImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SubmissionServiceImpl implements SubmissionService {
    @Resource
    private SubmissionDOMapper submissionDOMapper;

    @Autowired
    private ContestService contestService;

    @Autowired
    private ProblemService problemService;


    @Autowired
    private ValidatorImpl validator;

    /**
     * @param userId    用户id
     * @param problemId 题目id
     * @param language  语言
     * @param status    状态，对应数据库中的result
     * @return
     */
    @Override
    public List<SubmissionModel> querySubmissionList(String userId, Integer problemId, Short language,
                                                     Integer contestId, Integer status, Integer offset, Integer limit) {
        List<SubmissionDOWithBLOBs> submissionDOWithBLOBsList = submissionDOMapper
                .querySubmissionList(userId, problemId, language, contestId, status, offset, limit);
        List<SubmissionModel> submissionModelList = submissionDOWithBLOBsList.stream()
                .map(this::convertSubmissionModelFromSubmissionDOWithBLOBs).collect(Collectors.toList());
        return submissionModelList;
    }

    @Override
    public Integer querySubmissionCount(String userId, Integer problemId, Short language,
                                        Integer contestId, Integer status) {
        return submissionDOMapper.querySubmissionCount(userId, problemId, language, contestId, status);
    }

    @Override
    public SubmissionModel getSubmissionById(Integer runId) {
        SubmissionDOWithBLOBs submissionDOWithBLOBs = submissionDOMapper.selectByPrimaryKey(runId);
        return convertSubmissionModelFromSubmissionDOWithBLOBs(submissionDOWithBLOBs);
    }

    @Override
    public SubmissionModel createSubmission(SubmissionModel submissionModel) throws BusinessException {
        ValidationResult result = validator.validate(submissionModel);
        if (result.isHasError()) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
        }

        SubmissionDOWithBLOBs submissionDOWithBLOBs = convertSubmissionDOWithBLOBsFromSubmissionModel(submissionModel);

        submissionDOMapper.insertSelective(submissionDOWithBLOBs);

        return convertSubmissionModelFromSubmissionDOWithBLOBs(submissionDOWithBLOBs);
    }

    @Override
    public SubmissionModel updateSubmission(SubmissionModel submissionModel) {
        SubmissionDOWithBLOBs submissionDOWithBLOBs = convertSubmissionDOWithBLOBsFromSubmissionModel(submissionModel);
        submissionDOMapper.updateByPrimaryKeyWithBLOBs(submissionDOWithBLOBs);

        return convertSubmissionModelFromSubmissionDOWithBLOBs(submissionDOWithBLOBs);
    }

    @Override
    public List<SubmissionModel> getContestSubmission(Integer contestId, String userId) throws BusinessException {
        contestService.checkContestReadPermission(contestId, userId);
        List<SubmissionDOWithBLOBs> submissionDOWithBLOBsList = submissionDOMapper.querySubmissionList(null, null, null,
                contestId, null, null, null);
        List<SubmissionModel> submissionModelList = submissionDOWithBLOBsList.stream()
                .map(this::convertSubmissionModelFromSubmissionDOWithBLOBs).collect(Collectors.toList());
        return submissionModelList;
    }


    @Override
    public Set<Integer> getSubmitProblemIDList(String userId, Integer status) {

        List<Integer> problemIDList = submissionDOMapper.querySubmissionIDList(userId, null,
                null, null, status, 0, 10000);
        return new HashSet<>(problemIDList);
    }

    @Override
    public void reJudge(Integer contestId, String userId, Integer problemId) throws IOException {
        List<SubmissionModel> submissionModelList =
                querySubmissionList(userId, problemId, null, contestId, null, 0, 10000);

        updateStatus(contestId, userId, problemId, null, ConstUtil.JudgeStatus.Pending);

        AsyncUtils.getInstance().rejudgeServer(problemService, problemId, submissionModelList);
    }

    @Override
    public void reJudge(@NotNull Integer submissionId) throws IOException {
        SubmissionModel submissionModel = getSubmissionById(submissionId);
        ProblemModel problemModel = problemService.getProblemByID(submissionModel.getProblemId());
        //todo 计算正确人数的问题
        updateStatus(null, null, null, submissionId, ConstUtil.JudgeStatus.Pending);
        PushJudgerUtil.getInstance().pushJudge(submissionModel, problemModel);

    }

    private SubmissionModel convertSubmissionModelFromSubmissionDOWithBLOBs(SubmissionDOWithBLOBs submissionDOWithBLOBs) {
        if (submissionDOWithBLOBs == null) {
            return null;
        }

        SubmissionModel submissionModel = new SubmissionModel();
        BeanUtils.copyProperties(submissionDOWithBLOBs, submissionModel);
        return submissionModel;
    }

    private SubmissionDOWithBLOBs convertSubmissionDOWithBLOBsFromSubmissionModel(SubmissionModel submissionModel) {
        if (submissionModel == null) {
            return null;
        }

        SubmissionDOWithBLOBs submissionDOWithBLOBs = new SubmissionDOWithBLOBs();
        BeanUtils.copyProperties(submissionModel, submissionDOWithBLOBs);
        submissionDOWithBLOBs.setLanguage(Byte.parseByte(String.valueOf(submissionModel.getLanguage())));

        return submissionDOWithBLOBs;
    }

    private void updateStatus(Integer contestId, String userId, Integer problemId, Integer submissionId, @NotNull Integer status) {
        SubmissionDOExample example = new SubmissionDOExample();
        SubmissionDOExample.Criteria criteria = example.createCriteria();
        if (contestId != null) {
            criteria.andContestIdEqualTo(contestId);
        }
        if (!StringUtils.isEmpty(userId)) {
            criteria.andUserIdEqualTo(userId);
        }
        if (problemId != null) {
            criteria.andProblemIdEqualTo(problemId);
        }
        if (submissionId != null) {
            criteria.andRunIdEqualTo(submissionId);
        }
        SubmissionDOWithBLOBs submissionDOWithBLOBs = new SubmissionDOWithBLOBs();
        submissionDOWithBLOBs.setResult(Byte.parseByte(String.valueOf(status)));
        submissionDOMapper.updateByExampleSelective(submissionDOWithBLOBs, example);
    }


}
